<?php

/**
 * This is the model class for table "user_oauth".
 *
 * The followings are the available columns in table 'user_oauth':
 * @property integer $user_id
 * @property string $provider name of provider
 * @property string $identifier unique user authentication id that was returned by provider
 * @property string $profile_cache
 * @property string $session_data session data with user profile
 *
 *
 * @version 1.2.4
 * @copyright Copyright &copy; 2013 Sviatoslav Danylenko
 * @author Sviatoslav Danylenko <dev@udf.su> 
 * @license MIT ({@link http://opensource.org/licenses/MIT})
 * @link https://github.com/SleepWalker/hoauth
 */
class UserOAuth extends CActiveRecord {

    /**
     * @var $_hybridauth HybridAuth class instance
     */
    protected $_hybridauth;

    /**
     * @var $_adapter HybridAuth adapter	
     */
    protected $_adapter;

    /**
     * @var $_profileCache property for holding of unserialized 
     * 		profile cache copy
     */
    protected $_profileCache;

    /**
     * Returns the static model of the specified AR class.
     * @param string $className active record class name.
     * @return UserOAuth the static model class
     */
    public static function model($className = __CLASS__) {
        try {
            $model = parent::model($className);

            // db updates 'on the fly'
            $model->updateDb($model);

            return $model;
        } catch (CDbException $e) {
            self::createDbTable();
            Yii::app()->controller->refresh();
        }
    }

    /**
     * @return string the associated database table name
     */
    public function tableName() {
        if (!empty(Yii::app()->db->tablePrefix))
            return '{{user_oauth}}';
        else
            return 'user_oauth';
    }

    /**
     * @return array validation rules for model attributes.
     */
    public function rules() {
        return array();
    }

    public function afterFind() {
        parent::afterFind();

        if (!empty($this->profile_cache))
            $this->_profileCache = (object) unserialize($this->profile_cache);
    }

    public function beforeSave() {
        if (!empty($this->_profileCache))
            $this->profile_cache = serialize((array) $this->_profileCache);

        return parent::beforeSave();
    }

    /**
     * @static
     * @access public
     * @return configuration array of HybridAuth lib
     */
    public static function getConfig() {
        $config = self::getConfigPath();

        if (!file_exists($config))
            throw new CException("The config.php file doesn't exists");

        return require($config);
    }

    /**
     * @return path to the HybridAuth config file
     */
    public static function getConfigPath() {
//		$config = Yii::app()->params['hoauth']['configAlias'];
        $config = NULL;
        if (empty($config)) {
            $yiipath = Yii::getPathOfAlias('application.config.hoauth');
            $config = $yiipath . '.php';
           
        } else if (strpos($config, DIRECTORY_SEPARATOR) === false) {
         
            $config = Yii::getPathOfAlias($config) . '.php';
        }

        return $config;
    }

    /**
     * @access public
     * @return array of UserOAuth models
     */
    public function findUser($user_id, $provider = false) {
        $params = array('user_id' => $user_id);
        if ($provider) {
            $params['provider'] = $provider;
            return $this->findByAttributes($params);
        } else
            return $this->findAllByAttributes($params);
    }

    /**
     * @access public
     * @return Auth class. With restored users authentication session data
     * @link http://hybridauth.sourceforge.net/userguide.html
     * @link http://hybridauth.sourceforge.net/userguide/HybridAuth_Sessions.html
     */
    public function getHybridAuth() {
        if (!isset($this->_hybridauth)) {
            $path = dirname(__FILE__) . '/../hybridauth';
     
            require_once($path . '/Hybrid/Auth.php');
            $this->_hybridauth = new Hybrid_Auth(self::getConfig());

            if (!empty($this->session_data))
                $this->_hybridauth->restoreSessionData($this->session_data);
        }

        return $this->_hybridauth;
    }

    /**
     * @access public
     * @return Adapter for current provider or null, when we have no session data.
     * @link http://hybridauth.sourceforge.net/userguide.html
     */
    public function getAdapter() {
        if (!isset($this->_adapter) && isset($this->session_data) && isset($this->provider))
            $this->_adapter = $this->hybridAuth->getAdapter($this->provider);

        return $this->_adapter;
    }

    /**
     * authenticates user by specified adapter	
     * 
     * @param string $provider 
     * @access public
     * @return void
     */
    public function authenticate($provider) {
        if (empty($this->provider)) {
        
            try {
                $this->_adapter = $this->hybridauth->authenticate($provider);
                $this->identifier = $this->profile->identifier;
                $this->provider = $provider;

                $oAuth = self::model()->findByPk(array(
                    'provider' => $this->provider,
                    'identifier' => $this->identifier));

                if ($oAuth) {
                    $this->setAttributes($oAuth->attributes, false);
                } else {
                    $this->isNewRecord = true;
                }!

                $this->session_data = $this->hybridauth->getSessionData();
                return $this;
            } catch (Exception $e) {
                $error = "";
                switch ($e->getCode()) {
                    case 6 : //$error = "User profile request failed. Most likely the user is not connected to the provider and he should to authenticate again."; 
                    case 7 : //$error = "User not connected to the provider."; 
                        $this->logout();
                        return $this->authenticate($provider);
                        break;
                }
                throw $e;
            }
        }

        return null;
    }

    /**
     * Breaks HybridAuth session and logs user from sn out.
     *
     * @access public
     */
    public function logout() {
        if (!empty($this->_adapter)) {
            $this->_adapter->logout();
            $this->unsetAttributes();
        }
    }

    /**
     * @access public
     * @return Hybrid_User_Profile user social profile object
     */
    public function getProfile() {
        $profile = $this->adapter->getUserProfile();
        //caching profile
        $this->_profileCache = $profile;

        return $profile;
    }

    /**
     * binds local user to current provider 
     * 
     * @param mixed $user_id id of the user
     * @access public
     * @return whether the model successfully saved
     */
    public function bindTo($user_id) {
        $this->user_id = $user_id;
        return $this->save();
    }

    /**
     * @access public
     * @return whether this social network account bond to existing local account
     */
    public function getIsBond() {
        return !empty($this->user_id);
    }

    /**
     * Getter for cached profile.
     * We implement this method, 
     * because in older version of hoauth was no profile cache. 
     * So we need to fill db with caches
     * The second reason is camelCase
     */
    public function getProfileCache() {
        if (empty($this->_profileCache)) {
            $this->getProfile();
            $this->save();
        }

        return $this->_profileCache;
    }

    /**
     * creates table for holding provider bindings	
     */
    protected static function createDbTable() {
        $sql = file_get_contents(dirname(__FILE__) . '/user_oauth.sql');
        $sql = strtr($sql, array('{{user_oauth}}' => Yii::app()->db->tablePrefix . 'user_oauth'));
        Yii::app()->db->createCommand($sql)->execute();
    }

    /**
     * Runs DB updates on the fly
     */
    protected function updateDb($model) {
        $updates = array();
        // the try statement to correct my stupid column names in v1.0.1 of hoauth
        // sory about this
        try {
            $model->provider = $model->provider;
        } catch (Exception $e) {
            ob_start();
            ?>
            ALTER TABLE <?php echo '`' . $model->tableName() . '`'; ?> CHANGE `name` `provider` VARCHAR( 45 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
            CHANGE `value` `identifier` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
            <?php
            $updates[] = ob_get_clean();
        }

        // profile caching since v.1.2.2
        try {
            $model->profile_cache = $model->profile_cache;
        } catch (Exception $e) {
            ob_start();
            ?>
            ALTER TABLE <?php echo '`' . $model->tableName() . '`'; ?> ADD `profile_cache` TEXT NOT NULL AFTER `identifier`
            <?php
            $updates[] = ob_get_clean();
        }

        if (count($updates)) {
            foreach ($updates as $sql) {
                Yii::app()->db->createCommand($sql)->execute();
            }
            Yii::app()->controller->refresh();
        }
    }

    /**
     * @return array relational rules.
     */
    public function relations() {
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(
            'user' => array(self::BELONGS_TO, 'Users', 'id'),
        );
    }
}
